from google.appengine.ext import db
from django.utils import simplejson
import pprint
import datetime

class dbDebate(db.Model):
  creator = db.StringProperty()
  createtime = db.DateTimeProperty(auto_now_add=True)
  lasteditor = db.StringProperty()
  lastedittime = db.DateTimeProperty(auto_now_add=True)
  motion = db.StringProperty()
  summary = db.TextProperty()
  proposition = db.StringProperty()
  opposition = db.StringProperty()
  starttime = db.DateTimeProperty()
  endtime = db.DateTimeProperty()
  voteendtime = db.DateTimeProperty()
  forit = db.IntegerProperty()
  againstit = db.IntegerProperty()

class dbDebateHistory(db.Model):
  debate = db.ReferenceProperty(dbDebate)
  motion = db.StringProperty()
  summary = db.TextProperty()
  proposition = db.StringProperty()
  opposition = db.StringProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)
  forit = db.IntegerProperty()
  againstit = db.IntegerProperty()
  editor = db.StringProperty()

class dbUsersDebates(db.Model):
  userid = db.StringProperty()
  debate = db.ReferenceProperty(dbDebate)
  waveid = db.StringProperty()

class dbDebateVotes(db.Model):
  debate = db.ReferenceProperty(dbDebate)
  userid = db.StringProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)
  foragainst = db.IntegerProperty()

class dbPoints(db.Model):
  debate = db.ReferenceProperty(dbDebate)
  waveid = db.StringProperty()
  foragainst = db.IntegerProperty()
  pointtitle = db.StringProperty()
  point = db.StringProperty()
  counterpointtitle = db.StringProperty()
  counterpoint = db.StringProperty()
  rating = db.FloatProperty()
  creator = db.StringProperty()
  createtime = db.DateTimeProperty(auto_now_add=True)
  lasteditor = db.StringProperty()
  lastedittime = db.DateTimeProperty(auto_now_add=True)

class dbPointHistory(db.Model):
  point_key = db.ReferenceProperty(dbPoints)
  debate = db.ReferenceProperty(dbDebate)
  pointtitle = db.StringProperty()
  point = db.StringProperty()
  counterpointtitle = db.StringProperty()
  counterpoint = db.StringProperty()
  foragainst = db.IntegerProperty()
  rating = db.FloatProperty()
  editor = db.StringProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)

class dbPointRatings(db.Model):
  point = db.ReferenceProperty(dbPoints)
  userid = db.StringProperty()
  rate = db.IntegerProperty()

class dbPointRatingsHistory(db.Model):
  point_rate = db.ReferenceProperty(dbPointRatings)
  point = db.ReferenceProperty(dbPoints)
  userid = db.StringProperty()
  rate = db.IntegerProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)

class dbIndexes(db.Model):
  waveid = db.StringProperty()
  name = db.StringProperty()
  creator = db.StringProperty()

class dbParticipants(db.Model):
  uid = db.StringProperty()
  name = db.StringProperty()
  country = db.StringProperty()
  email = db.StringProperty()
  userid = db.StringProperty()
  index = db.ReferenceProperty(dbIndexes)

class dbAdminUsers(db.Model):
  userid = db.StringProperty()

class dbBlips(db.Model):
  waveid = db.StringProperty()
  blipid = db.StringProperty()
  bliptext = db.TextProperty()

class dbQueue(db.Model):
  queue = db.StringProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)
  data = db.TextProperty()

class dbBlipRates(db.Model):
  waveid = db.StringProperty()
  point = db.ReferenceProperty(dbPoints)
  blipid = db.StringProperty()
  creator = db.StringProperty()
  viewer = db.StringProperty()
  like = db.IntegerProperty()

class dbBlipRateHistory(db.Model):
  waveid = db.StringProperty()
  point = db.ReferenceProperty(dbPoints)
  blipid = db.StringProperty()
  creator = db.StringProperty()
  viewer = db.StringProperty()
  like = db.IntegerProperty()
  timestamp = db.DateTimeProperty(auto_now_add=True)

class dbExchange(db.Model):
  keys = db.StringProperty()
  waveid = db.StringProperty()
  userid = db.StringProperty()

## After this it's all a wall :O

class voteDB():

  @staticmethod
  def ReturnOne(qobj):
    r = qobj.fetch(1)
    if len(r) > 0:
      return r[0]
    else:
      return None

  @staticmethod
  def DebateIndex():
    return dbDebate.all()

  @staticmethod
  def DebateIndexes():
    return dbIndexes.all()

  @staticmethod
  def DebateIndexAdd(waveid, name, creator):
    i = dbIndexes()
    i.waveid = waveid
    i.name = name
    i.creator = creator
    return i.put()

  @staticmethod
  def Participants():
    return dbParticipants.all()

  @staticmethod
  def ParticipantsNoIndex():
    q = dbParticipants.gql("WHERE index = NULL")
    return q.fetch(1000)

  @staticmethod
  def DebateParticipants(indx):
    q = dbParticipants.gql("WHERE index = :1", indx)
    peeps = q.fetch(1000)
    rtv = []
    for p in peeps:
      rtv.append({
        'uid': p.uid,
        'country': p.country,
        'email': p.email,
        'name': p.name,
        'userid': p.userid
      })
    return rtv

  @staticmethod
  def ParticipantAdd(uid, name, country, email, userid):
    rv = 1
    q = dbParticipants.gql("WHERE uid = :1", uid)
    p = voteDB.ReturnOne(q)
    if p == None:
      rv = 0
      p = dbParticipants()
    p.uid = uid
    p.name = name
    p.country = country
    p.email = email
    p.userid = userid
    p.put()
    return rv

  @staticmethod
  def DebateIndexAddParticipant(waveid, participant, name):
    q = dbIndexes.gql("WHERE waveid = :1", waveid)
    indx = voteDB.ReturnOne(q)
    if indx != None:
      p = dbParticipants()
      p.index = indx
      p.userid = participant
      p.name = name
      rtv = p.put()
    else:
      rtv = 0
    return rtv

  @staticmethod
  def UsersDebateGet(userid, waveid):
    q = db.GqlQuery("SELECT * FROM dbUsersDebates WHERE userid = :1 AND waveid = :2", userid, waveid)
    r = voteDB.ReturnOne(q)
    if r != None:
      return r.debate
    else:
      return None

  @staticmethod
  def UsersDebateAdd(userid, debate_key, waveid):
    dud = dbUsersDebates()
    dud.userid = userid
    dud.debate = debate_key
    dud.waveid = waveid
    k = dud.put()
    return k

  @staticmethod
  def DebateGet(userid, waveid):
    r = voteDB.UsersDebateGet(userid, waveid)
    if r != None:
      return db.get(r.debate)

  @staticmethod
  def DebateAdd(motion, summary, proposition, opposition, starttime, endtime, voteendtime, creator):
    deb = dbDebate()
    deb.creator = creator
    deb.lasteditor = creator
    deb.motion = motion
    deb.summary = summary
    deb.proposition = proposition
    deb.opposition = opposition
    deb.starttime = starttime
    deb.endtime = endtime
    deb.voteendtime = voteendtime
    deb.forit = 0
    deb.againstit = 0
    deb.put()
    voteDB.DebateHistoryAdd(deb.key(), motion, summary, proposition, opposition, creator, 0, 0)
    return deb.key()

  @staticmethod
  def DebateHistoryAdd(debate_key, motion, summary, proposition, opposition, userid, forit, againstit):
    debhist = dbDebateHistory()
    debhist.debate = debate_key
    debhist.motion = motion
    debhist.summary = summary
    debhist.proposition = proposition
    debhist.opposition = opposition
    debhist.editor = userid
    debhist.forit = forit
    debhist.againstit = againstit
    return debhist.put()

  @staticmethod
  def DebateEdit(debate_key, motion, summary, proposition, opposition, userid):
    deb_key = db.Key(debate_key)
    deb = db.get(deb_key)
    deb.motion = motion
    deb.summary = summary
    deb.proposition = proposition
    deb.opposition = opposition
    deb.lasteditor = userid
    deb.lastedittime = datetime.datetime.utcnow()
    deb.put()
    return voteDB.DebateHistoryAdd(deb_key, motion, summary, proposition, opposition, userid, deb.forit, deb.againstit)

  @staticmethod
  def DebateRollback(debate_key):
    deb_key = db.Key(debate_key)
    deb = db.get(deb_key)
    deb_old = dbDebateHistory.gql("WHERE debate = :1 ORDER BY timestamp DESC LIMIT 1,1", deb_key).fetch(1)[0]
    return voteDB.DebateEdit(debate_key, deb_old.motion, deb_old.summary, deb_old.proposition, deb_old.opposition, deb_old.editor)

  @staticmethod
  def DebatePointsGet(debate_key):
    return db.GqlQuery("SELECT * FROM dbPoints WHERE debate = :1", debate_key)

  @staticmethod
  def Points():
    return dbPoints.all()

  @staticmethod
  def PointAdd(debate_key, waveid, point, pointtitle, foragainst, userid):
    deb_key = db.Key(debate_key)
    forit = int(foragainst)
    pnt = dbPoints()
    pnt.debate = deb_key
    pnt.waveid = waveid
    pnt.foragainst = forit
    pnt.creator = userid
    pnt.lasteditor = userid
    pnt.pointtitle = pointtitle
    pnt.point = point
    pnt.counterpointtitle = ""
    pnt.counterpoint = ""
    pnt.rating = 0.0
    pnt.put()
    voteDB.PointHistoryAdd(pnt.key(), deb_key, point, pointtitle, "", "", userid, forit, 0.0)
    return pnt.key()

  @staticmethod
  def PointHistoryAdd(point_key, debate_key, point, pointtitle, counterpoint, counterpointtitle, userid, foragainst, rating):
    hist = dbPointHistory()
    hist.point_key = point_key
    hist.debate = debate_key
    hist.point = point
    hist.pointtitle = pointtitle
    hist.counterpoint = counterpoint
    hist.counterpointtitle = counterpointtitle
    hist.foragainst = foragainst
    hist.rating = rating
    hist.editor = userid
    return hist.put()

  @staticmethod
  def PointEdit(point_key, point, pointtitle, counterpoint, counterpointtitle, userid, foragainst):
    forit = int(foragainst)
    # pnt_key = db.Key(point_key)
    pnt_key = point_key
    pnt = db.get(pnt_key)
    pnt.point = point
    pnt.pointtitle = pointtitle
    pnt.counterpoint = counterpoint
    pnt.counterpointtitle = counterpointtitle
    pnt.foragainst = forit
    pnt.lasteditor = userid
    pnt.lastedittime = datetime.datetime.utcnow()
    pnt.put()
    return voteDB.PointHistoryAdd(pnt_key, pnt.debate, point, pointtitle, counterpoint, counterpointtitle, userid, forit, pnt.rating)

  @staticmethod
  def PointRollback(point_key):
    point_key = db.Key(point_key)
    pnt = db.get(point_key)
    pnt_old = dbPointHistory.gql("WHERE point_key = :1 ORDER BY timestamp DESC LIMIT 1,1", point_key).fetch(1)[0]
    return voteDB.PointEdit(point_key, pnt_old.point, pnt_old.pointtitle, pnt_old.counterpoint, pnt_old.counterpointtitle, pnt_old.editor, pnt_old.foragainst)

  @staticmethod
  def PointGet(waveid):
    pnts = dbPoints.all()
    return pnts.filter('waveid=', waveid).fetch(1)

  @staticmethod
  def PointUpdateWaveID(point_key, waveid):
    pnt = voteDB.GetRawKey(point_key)
    if pnt != None:
      pnt.waveid = waveid
      pnt.put()
      return True
    else:
      return None

  @staticmethod
  def PointUserRateGet(point_key, userid):
    # pnt_key = db.Key(point_key)
    pnt_key = point_key
    return voteDB.ReturnOne(db.GqlQuery("SELECT * FROM dbPointRatings WHERE point = :1 AND userid = :2", pnt_key, userid))

  @staticmethod
  def PointUserRateHistAdd(point_rate, point, userid, rate):
    puh = dbPointRatingsHistory()
    puh.point_rate = point_rate
    puh.point = point
    puh.userid = userid
    puh.rate = rate
    return puh.put()

  @staticmethod
  def PointRate(point_key, userid, rate):
    currate = voteDB.PointUserRateGet(point_key, userid)
    if currate != None:
      voteDB.PointUserRateHistAdd(currate.key(), point_key, userid, currate.rate)
      currate.rate = int(rate)
      pr_key = currate.put()
    else:
      pr = dbPointRatings()
      pr.point = point_key
      pr.userid = userid
      pr.rate = int(rate)
      pr_key = pr.put()
    voteDB.PointRateUpdate(point_key)
    pnt = db.get(point_key)
    voteDB.PointHistoryAdd(point_key, pnt.debate, pnt.point, pnt.pointtitle, pnt.counterpoint, pnt.counterpointtitle, userid, pnt.foragainst, pnt.rating)
    return 1 ## TODO - Better rtv

  @staticmethod
  def PointRateUpdate(point_key):
    rates = db.GqlQuery("SELECT * FROM dbPointRatings WHERE point = :1 ", point_key).fetch(1000)
    a = 0
    t = 0
    for r in rates:
      a = a + 1
      t = t + int(r.rate)
    rate = t / a
    pnt = db.get(point_key)
    pnt.rating = float(rate)
    return pnt.put()

  @staticmethod
  def DebateDel(waveid):
    deb = voteDB.DebateGet(waveid)
    if deb != None:
      deb.delete()
      query = db.GqlQuery("SELECT * FROM dbPoints WHERE debate = :1", deb.key())
      for res in query:
        res.delete()
      return True
    else:
      return None

  @staticmethod
  def DebateVotesGet(debate_key, userid):
    deb_key = db.Key(debate_key)
    gotone = voteDB.ReturnOne(db.GqlQuery("SELECT * FROM dbDebateVotes WHERE userid = :1 AND debate = :2 ORDER BY timestamp DESC", userid, deb_key))
    if gotone != None:
      uservote = gotone.foragainst
    else:
      uservote = "NA"
    deb = db.get(deb_key)
    if deb == None:
      return {'error': "Invalid data"}
    return {'forit': deb.forit, 'againstit': deb.againstit, 'yourvote': uservote}

  @staticmethod
  def DebateVotes(debate_key):
    return db.GqlQuery("SELECT * FROM dbDebateVotes WHERE debate = :1", db.Key(debate_key)).fetch(1000)

  @staticmethod
  def DebateVote(debate_key, userid, vote):
    vot = int(vote)
    deb_key = db.Key(debate_key)
    deb = db.get(deb_key)
    if deb == None:
      return {'error': "Invalid data"}
    gotone = voteDB.ReturnOne(db.GqlQuery("SELECT * FROM dbDebateVotes WHERE userid = :1 AND debate = :2 ORDER BY timestamp DESC", userid, deb_key))
    # return pprint.pformat(gotone)
    updatevote = 0
    if gotone != None:
      curvot = int(gotone.foragainst)
      newvote = 0
      if curvot != vot:
        updatevote = 1
      else:
        updatevote = 0
    else:
      newvote = 1

    if updatevote == 1 or newvote == 1:
      dv = dbDebateVotes()
      dv.userid = userid
      dv.debate = deb_key
      dv.foragainst = vot
      dv.put()

      if vot == 1:
        deb.forit = deb.forit + 1
        if newvote == 0:
          deb.againstit = deb.againstit - 1
      else:
        deb.againstit = deb.againstit + 1
        if newvote == 0:
          deb.forit = deb.forit - 1
      deb.put()
    return 1

  @staticmethod
  def PointGet(waveid):
    q = db.GqlQuery("SELECT * FROM dbPoints WHERE waveid = :1", waveid)
    return voteDB.ReturnOne(q)

  @staticmethod
  def GetRawKey(point_key):
    return db.get(point_key)

  @staticmethod
  def PointDel(point_key):
    pnt = voteDB.GetRawKey(point_key)
    pnt.delete()

  @staticmethod
  def PointAdjust(point_key, foragaint, amount):
    pnt = db.get(point_key)
    if foragainst == TRUE:
      pnt.forit = pnt.forit + amount
    else:
      pnt.againstit = pnt.againstit + amount
    pnt.put()

  @staticmethod
  def Vote(userid, point_key, foragainst):
    if foragainst == TRUE:
      forit = TRUE
    else:
      forit = FALSE
    query = db.GqlQuery("SELECT * FROM dbVotes WHERE point = :1 AND userid = :2", point_key, userid)
    if query.count() == 0:
      # New Vot
      vot = dbVotes()
      vot.point = point_key
      vot.foragainst = forit
      vot.userid = userid
      vot.put()
      voteDB.PointAdjust(point_key, forit, 1)
    else:
      # old Vote.
      res = query.fetch(1)
      if res[0].foragainst != forit:
        voteDB.PointAdjust(point_key, res.foragainst, -1)
        voteDB.PointAdjust(point_key, forit, 1)
        res.foragainst = forit
        res.put()

  @staticmethod
  def AdminCheck(userid):
    q = db.GqlQuery("SELECT * FROM dbAdminUsers WHERE userid = :1", userid)
    return voteDB.ReturnOne(q)

  @staticmethod
  def AdminAdd(userid):
    dbau = dbAdminUsers()
    dbau.userid = userid
    return dbau.put()

  @staticmethod
  def AdminDel(userid):
    ad = voteDB.AdminCheck(userid)
    if ad != None:
      ad.delete()
      return True
    else:
      return False

  @staticmethod
  def AdminUsers():
    return dbAdminUsers.all()

  @staticmethod
  def BlipAdd(waveid, blipid, bliptext):
    b = dbBlips()
    b.waveid = waveid
    b.blipid = blipid
    b.bliptext = bliptext
    return b.put()

  @staticmethod
  def BlipRate(waveid, point, blipid, creator, viewer, like):
    li = int(like)
    # fugly
    if li == 1:
      l = 1
    elif li == -1:
      l = -1
    else:
      l = 0
    br = voteDB.ReturnOne(dbBlipRates.gql("WHERE blipid = :1 AND viewer = :2", blipid, viewer))
    if br == None:
      br = dbBlipRates()
    br.waveid = waveid
    br.point = point
    br.blipid = blipid
    br.creator = creator
    br.viewer = viewer
    br.like = l
    br.put()
    return voteDB.BlipRateHistoryAdd(waveid, point, blipid, creator, viewer, l)

  @staticmethod
  def BlipRateHistoryAdd(waveid, point, blipid, creator, viewer, like):
    br = dbBlipRateHistory()
    br.waveid = waveid
    br.point = point
    br.blipid = blipid
    br.creator = creator
    br.viewer = viewer
    br.like = like
    return br.put()

  @staticmethod
  def WorkLen(q):
    w = dbQueue.all()
    return w.filter("queue =", q).count()

  @staticmethod
  def WorkAdd(q, dat):
    w = dbQueue()
    w.queue = q
    w.data = simplejson.dumps(dat)
    return w.put()

  @staticmethod
  def WorkGet(q, num):
    r = dbQueue.all()
    rv = r.filter("queue =", q).order("-timestamp").fetch(num)
    rtv = []
    for r in rv:
      rtv.append(simplejson.loads(r.data))
      r.delete()
    return rtv

  @staticmethod
  def ExchangeAdd(key, waveid, userid):
    e = dbExchange()
    e.keys = key
    e.waveid = waveid
    e.userid = userid
    return e.put()

  @staticmethod
  def ExchangeGet(type, val):
    q = dbExchange.all().filter(type + " =", val)
    r = voteDB.ReturnOne(q)
    if r != None:
      rtv = {'key': r.keys, 'waveid': r.waveid, 'userid': r.userid}
    else:
      rtv = {'error': 'Not found'}
    return rtv
